﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Configuration;

namespace ConversionInterface
{
    public class MediaFile
    {
        public enum MediaType
        {
            MKV,
            AVI,
            MPEG4,
            OGG
        }
        public enum MediaLanguage
        {
            English,
            Spanish,
            French,
            Japanese,
            Undefined,
            None
        }
        private Configuration config;

        public MediaType Type { get; set; }
        public VideoTrack Video { get; set; }
        public List<AudioTrack> AvailableAudio { get; set; }
        public List<SubtitleTrack> AvailableSubs { get; set; }
        public AudioTrack ChosenAudio { get; set; }
        public SubtitleTrack ChosenSub { get; set; }
        public string InputFilename { get; set; }
        public string OutputFilename { get; set; }
        public string ShortFilename { get; set; }
        public ConvertMedia.ConversionTarget TargetDevice { get; set; }
        public int PercentComplete { get; set; }
        public string StageInfo { get; set; }
        public int JobId { get; set; }
        
        public MediaFile(string filename)
        {
            FileInfo fileInfo = new FileInfo(filename);
            if (!fileInfo.Exists)
                throw new ArgumentException(filename + " does not exist");

            config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);


            this.Video = new VideoTrack();
            this.AvailableAudio = new List<AudioTrack>();
            this.AvailableSubs = new List<SubtitleTrack>();
            this.InputFilename = fileInfo.FullName;
            this.ShortFilename = fileInfo.Name;

            string defaultTarget = config.AppSettings.Settings["TargetPlatform"].Value;
            string outputExtension = "";
            switch (defaultTarget)
            {
                case "iPhone":
                case "iPodTouch":
                    this.TargetDevice = ConvertMedia.ConversionTarget.iPhone;
                    outputExtension = ".mp4";
                    break;
                default:
                    throw new ArgumentException("Target Device " + defaultTarget + " is not supported at this time");
            }

            this.OutputFilename = this.ShortFilename.Substring(0, this.ShortFilename.LastIndexOf('.')) + outputExtension;

            ProcessStartInfo startInfo = new ProcessStartInfo("MediaInfo.exe", "\"" + filename +"\"");
            startInfo.RedirectStandardError = true;
            startInfo.RedirectStandardInput = true;
            startInfo.RedirectStandardOutput = true;
            startInfo.CreateNoWindow = true;
            startInfo.UseShellExecute = false;
            Process process = new Process();

            process.StartInfo = startInfo;
            process.EnableRaisingEvents = true;

            if (process.Start())
            {
                StreamReader error = process.StandardError;
                StreamReader output = process.StandardOutput;
                StreamWriter input = process.StandardInput;
                ParseResults(output.ReadToEnd());
            }

        }

        private void ParseResults(string results)
        {
            string[] lines = results.Split(new string[] { "\r\r\n\r\r\n" }, StringSplitOptions.None);
            List<Dictionary<string, string>> audio = new List<Dictionary<string, string>>();
            List<Dictionary<string, string>> subs = new List<Dictionary<string, string>>();
            Dictionary<string, string> general = null;
            Dictionary<string, string> video = null;
            int track = 1;

            foreach (string s in lines)
            {
                if (s.StartsWith("General"))
                {
                    general = GetDictionary(s);
                }
                else if (s.StartsWith("Video"))
                {
                    video = GetDictionary(s);

                }
                else if (s.StartsWith("Audio"))
                {
                    audio.Add(GetDictionary(s));
                }
                else if (s.StartsWith("Text"))
                {
                    subs.Add(GetDictionary(s));
                }
            }

            DecipherGeneral(general);
            DecipherVideo(video);
            DecipherAudio(audio);
            DecipherSubs(subs);

            //Add dummy tracks for No Audio and No Sub, and grab the default Languages from user settings
            AudioTrack noAudioTrack = new AudioTrack();
            noAudioTrack.Language = MediaLanguage.None;
            SubtitleTrack noSubTrack = new SubtitleTrack();
            noSubTrack.Language = MediaLanguage.None;
            this.AvailableAudio.Add(noAudioTrack);
            this.AvailableSubs.Add(noSubTrack);
            string defaultAudioLanguage = config.AppSettings.Settings["AudioLanguage"].Value;
            string defaultSubLanguage = config.AppSettings.Settings["SubtitleLanguage"].Value;

            //See if the default audiotrack is offered, and choose it
            foreach (AudioTrack audioTrack in this.AvailableAudio)
            {
                if (audioTrack.Language.ToString() == defaultAudioLanguage)
                {
                    this.ChosenAudio = audioTrack;
                    break;
                }
            }

            //See if default sub track is offered, and choose it
            foreach (SubtitleTrack subTrack in this.AvailableSubs)
            {
                if (subTrack.Language.ToString() == defaultSubLanguage)
                {
                    this.ChosenSub = subTrack;
                    break;
                }
            }

            //if nothing has been chosen, pick the first available
            if (this.ChosenAudio == null && this.AvailableAudio.Count > 0 && this.AvailableAudio.Count <= 2)
                this.ChosenAudio = this.AvailableAudio[0];
            
            //if nothing has been chosen, pick the first available
            if (this.ChosenSub == null && this.AvailableSubs.Count > 0 && this.AvailableSubs.Count <= 2)
                this.ChosenSub = this.AvailableSubs[0];

            //Give Video, Audio, and Subtracks sequential track numbers, based on the order they appeared in MediaInfo
            this.Video.TrackNumber = 0;
            track = 0;
            foreach (AudioTrack audioTrack in this.AvailableAudio)
                audioTrack.TrackNumber = track++;
            track = 0;
            foreach (SubtitleTrack subTrack in this.AvailableSubs)
                subTrack.TrackNumber = track++;
        }

        private void DecipherGeneral(Dictionary<string, string> general)
        {
            if (general == null)
                throw new ArgumentException("No information about media file found");

            string type = "";

            if (general.ContainsKey("Format"))
                type = general["Format"];

            switch (type)
            {
                case "Matroska":
                    this.Type = MediaFile.MediaType.MKV;
                    break;
                case "AVI":
                    this.Type = MediaType.AVI;
                    break;
                case "MPEG-4":
                    this.Type = MediaType.MPEG4;
                    break;
                case "OGG":
                    this.Type = MediaType.OGG;
                    break;
                default:
                    throw new ArgumentException("Unsupported file type: " + type);
            }

        }

        private void DecipherVideo(Dictionary<string, string> video)
        {
            if (video == null)
                throw new ArgumentException("No video found");

            string format = "";

            if(video.ContainsKey("Format"))
                format = video["Format"];
            
            switch (format)
            {
                case "AVC":
                    this.Video.Codec = VideoTrack.VideoCodec.H264;
                    break;
                case "MPEG-4 Visual":
                    this.Video.Codec = VideoTrack.VideoCodec.MPEG4V;
                    break;
                default:
                    throw new ArgumentException("Unsupported video codec: " + format);
            }


            string bitrate = this.ParsePropertyString("Nominal bit rate",video).Replace("Kbps", "").Trim();
            string width = this.ParsePropertyString("Width",video).Replace("pixels", "").Trim();
            string height = this.ParsePropertyString("Height",video).Replace("pixels", "").Trim();
            this.Video.AspectRatio = this.ParsePropertyString("Display aspect ratio",video);
            string fps = this.ParsePropertyString("Frame rate",video).Replace("fps", "").Trim();
            int nBitRate, nWidth, nHeight;
            decimal dFPS;
            if (Int32.TryParse(bitrate, out nBitRate))
                this.Video.BitRate = nBitRate;
            if (Int32.TryParse(width, out nWidth))
                this.Video.Width = nWidth;
            if(Int32.TryParse(height, out nHeight))
                this.Video.Height = nHeight;
            if(Decimal.TryParse(fps,out dFPS))
                this.Video.FPS = dFPS;
        }

        private void DecipherAudio(List<Dictionary<string, string>> audio)
        {
            if (audio.Count == 0)
                throw new ArgumentException("No audio found");

            foreach (Dictionary<string, string> d in audio)
            {
                AudioTrack track = new AudioTrack();
                string format = "";
                string language = "";
                if (d.ContainsKey("Format"))
                    format = d["Format"];

                if (d.ContainsKey("Language"))
                    language = d["Language"];

                switch (format)
                {
                    case "AAC":
                        track.Codec = AudioTrack.AudioCodec.AAC;
                        break;
                    case "MPEG Audio":
                        track.Codec = AudioTrack.AudioCodec.MPEG;
                        break;
                    case "Vorbis":
                        track.Codec = AudioTrack.AudioCodec.Vorbis;
                        break;
                    case "AC-3":
                        track.Codec = AudioTrack.AudioCodec.AC3;
                        break;
                    case "DTS":
                        track.Codec = AudioTrack.AudioCodec.DTS;
                        break;
                    default:
                        throw new ArgumentException("Audio Codec Not Supported: " + format);
                }

                switch (language.ToLower())
                {
                    case "english":
                        track.Language = MediaFile.MediaLanguage.English;
                        break;
                    case "french":
                        track.Language = MediaFile.MediaLanguage.French;
                        break;
                    case "japanese":
                        track.Language = MediaFile.MediaLanguage.Japanese;
                        break;
                    case "spanish":
                        track.Language = MediaFile.MediaLanguage.Spanish;
                        break;
                    default:
                        track.Language = MediaFile.MediaLanguage.Undefined;
                        break;
                }

                this.AvailableAudio.Add(track);
            }
        }

        private void DecipherSubs(List<Dictionary<string, string>> subs)
        {
            if (subs.Count == 0)
                return;

            foreach (Dictionary<string, string> d in subs)
            {
                SubtitleTrack track = new SubtitleTrack();
                string format = "";
                string language = "";

                if (d.ContainsKey("Format"))
                    format = d["Format"];

                if (d.ContainsKey("Language"))
                    language = d["Language"];

                switch (format)
                {
                    case "ASS":
                        track.Type = SubtitleTrack.SubtitleType.ASS;
                        break;
                    default:
                        track.Type = SubtitleTrack.SubtitleType.Unknown;
                        break;
                }

                switch (language.ToLower())
                {
                    case "english":
                        track.Language = MediaFile.MediaLanguage.English;
                        break;
                    case "french":
                        track.Language = MediaFile.MediaLanguage.French;
                        break;
                    case "japanese":
                        track.Language = MediaFile.MediaLanguage.Japanese;
                        break;
                    case "spanish":
                        track.Language = MediaFile.MediaLanguage.Spanish;
                        break;
                    default:
                        track.Language = MediaFile.MediaLanguage.Undefined;
                        break;
                }
                this.AvailableSubs.Add(track);
            }

        }

        private Dictionary<string, string> GetDictionary(string wholeSection)
        {
            string[] section = wholeSection.Split(new string[] { "\r\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            Dictionary<string, string> d = new Dictionary<string, string>();
            foreach (string s in section)
            {
                if (s.IndexOf(":") > 0)
                {
                    string[] pieces = s.Split(new char[] { ':' }, 2);
                    if (pieces.Length == 2)
                    {
                        d.Add(pieces[0].Trim(), pieces[1].Trim());
                    }
                }
            }
            return d;
        }

        private string ParsePropertyString(string property, Dictionary<string,string> dictionary)
        {
            if (dictionary.ContainsKey(property))
            {
                return dictionary[property];
            }
            else
            {
                return "";
            }
        }
    }
}
